package com.course.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * @author: Durian
 * @date: 2021/6/5 22:09
 */
@Slf4j
@Component
public class UserLoginGatewayFilter implements GatewayFilter, Ordered {

    @Resource(name = "jsonRedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;
    private static LinkedList<String> excludes;

    @PostConstruct
    public void init() {
        excludes = new LinkedList<>();
        excludes.add("/system/admin/users/login");
        excludes.add("/system/captcha");
        excludes.add("/system/users/logout");
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        for (String exclude : excludes) {
            if (path.contains(exclude)) {
                log.info("请求path[{}]被排除", path);
                return chain.filter(exchange);
            }
        }
        String token = exchange.getRequest().getHeaders().getFirst("token");
        log.info("请求拦截: path=[{}], token = [{}]", path, token);
        // 没有token
        if (StringUtils.isBlank(token)) {
            log.info("请求path[{}]的token为空", path);
            return errorInfo(exchange, "未登录", HttpStatus.UNAUTHORIZED.value());
        }
        // 验证token
        Object userInfo = redisTemplate.opsForValue().get(token);
        if (userInfo == null) {
            log.info("请求path[{}]的token[{}]无效", path, token);
            return errorInfo(exchange, "未登录", HttpStatus.UNAUTHORIZED.value());
        }
        // 验证通过
        log.info("token[{}]验证通过", token);
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 1;
    }


    private Mono<Void> errorInfo(ServerWebExchange exchange, String message, Integer status) {
        // 自定义返回格式
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("success", false);
        resultMap.put("code", status == null ? 2 : status);
        resultMap.put("msg", StringUtils.isBlank(message) ? "服务异常！" : message);
        return Mono.defer(() -> {
            byte[] bytes;
            try {
                bytes = new ObjectMapper().writeValueAsBytes(resultMap);
            } catch (JsonProcessingException e) {
                log.error("网关响应异常：", e);
                throw new RuntimeException("信息序列化异常");
            } catch (Exception e) {
                log.error("网关响应异常：", e);
                throw new RuntimeException("写入响应异常");
            }
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON.toString());
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        });
    }

}
